package wickethub.web.page;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.wicket.PageParameters;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.form.AjaxFallbackButton;
import org.apache.wicket.behavior.HeaderContributor;
import org.apache.wicket.extensions.markup.html.captcha.CaptchaImageResource;
import org.apache.wicket.extensions.markup.html.tabs.AbstractTab;
import org.apache.wicket.extensions.markup.html.tabs.ITab;
import org.apache.wicket.extensions.markup.html.tabs.TabbedPanel;
import org.apache.wicket.markup.html.IHeaderContributor;
import org.apache.wicket.markup.html.IHeaderResponse;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.RequiredTextField;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.image.Image;
import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.CompoundPropertyModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.model.ResourceModel;
import org.apache.wicket.model.StringResourceModel;
import org.apache.wicket.validation.validator.StringValidator;

import wickethub.data.DataHolder;
import wickethub.data.Util;
import wickethub.domain.BlogPost;
import wickethub.domain.Component;
import wickethub.domain.Resource;
import wickethub.domain.Snippet;
import wickethub.repository.ResourceRepository;
import wickethub.web.components.autocompleter.AutocompleterBehavior;

import com.google.inject.Inject;

public class EditResourcePage extends BasePage {
	
	private static final Log log = LogFactory.getLog(EditResourcePage.class);
	
	@Inject	private ResourceRepository repo;
	@Inject private DataHolder holder;
	
	private IModel<Resource> model = new Model<Resource>(new Component()); // component is default tab
	private boolean newResource = false;
	
	private String imagePass = randomString();
	private String captchaVerification;
	private final CaptchaImageResource captchaImageResource = new CaptchaImageResource(imagePass);
	
	public static final String TOPICS = "topics";
	public static final String VERSIONS = "versions";
	
	public EditResourcePage() {
		newResource = true;
		processPage();
	}
	
	public EditResourcePage(IModel<Resource> model) {
		this.model = model;
		processPage();
	}
	
	private void processPage() {

		setTitle(new StringResourceModel((newResource ? "add" : "edit") + ".page.title", this, model));
		
		add(new HeaderContributor(new IHeaderContributor() {
			public void renderHead(IHeaderResponse response) {
				response.renderJavascript("var " + TOPICS + " = "+ Util.getJsonFromSet(holder.getTopics()) + ";", null);
				response.renderJavascript("var " + VERSIONS + " = "+ Util.getJsonFromSet(holder.getVersions()) + ";", null);
			}
		}));
	    
		final Form<Resource> form = new Form<Resource>("form", new CompoundPropertyModel<Resource>(model)) {
			@Override
			protected void onSubmit() {
        		if (!imagePass.equals(captchaVerification)) {
        			error(getString("error.captcha"));
        		} else {
        			captchaImageResource.invalidate();
        			Resource resource = model.getObject();
        			
        			// FIXME this is a workaround, because this onSubmit is called before the different panel forms' onSubmit ... (ask on mailing list) 
    				if (BlogPost.class.isAssignableFrom(resource.getClass())) {
    					BlogPost bp = (BlogPost) resource;
    					String title = Util.getTitleFromWebPage(bp.getUrl());
    					if (title == null) {
    						title = Util.getStringForResourceType(resource);
    					}
    					resource.setTitle(title);
    				} else if (Snippet.class.isAssignableFrom(resource.getClass())) {
    					resource.setTitle(resource.getDescription() != null ? Util.abbreviate(resource.getDescription()) : Util.getStringForResourceType(resource));
    				}
        			
            		resource.touch();
            		repo.save(resource);
            		setResponsePage(ViewResourcePage.class,
    		    			new PageParameters("0=" + Util.getTextForUrl(resource.getTitle()) + ",1=" + resource.getId()));
            		getSession().info(getString("submission.thankyou"));
        		}
        	}
		};
		
		final FeedbackPanel feedback = new FeedbackPanel("feedback");
		feedback.setOutputMarkupId(true);
		form.add(feedback);
		
		form.add(new WebMarkupContainer("addMessage") {
			@Override
			public boolean isVisible() {
				return newResource;
			}
		});
		
		if (!newResource) {
			form.add(getResourcePanel("panel", model.getObject().getClass()));
		} else {
			List<ITab> tabs = new ArrayList<ITab>();
			tabs.add(new AbstractTab(new ResourceModel("resource.component")) {
				@Override
				public Panel getPanel(String panelId) {
					return getResourcePanel(panelId, Component.class);
				}
			});
			tabs.add(new AbstractTab(new ResourceModel("resource.blogpost")) {
				@Override
				public Panel getPanel(String panelId) {
					EditResourcePage.this.model = new Model<Resource>(new BlogPost());
					return getResourcePanel(panelId, BlogPost.class);
				}
			});
			tabs.add(new AbstractTab(new ResourceModel("resource.snippet")) {
				@Override
				public Panel getPanel(String panelId) {
					EditResourcePage.this.model = new Model<Resource>(new Snippet());
					return getResourcePanel(panelId, Snippet.class);
				}
			});
			
			form.add(new TabbedPanel("panel", tabs));
		}
		
		final TextField<String> topicsTextField = new TextField<String>("topics", new Model<String>() {
			@Override
			public String getObject() {
				return model.getObject().getTopics();
			}
        	@Override
        	public void setObject(String object) {
        		Set<String> set = Util.getSetFromString(object);
        		model.getObject().setTopics(Util.getStringFromSet(set));
        		holder.addTopics(set); // also add to the singleton that holds 'the' collection of topics
        	}
        });
		
		topicsTextField.add(new AutocompleterBehavior(TOPICS));
        form.add(topicsTextField);
        
        final TextField<String> versionsTextField = new TextField<String>("versions", new Model<String>() {
			@Override
			public String getObject() {
				return model.getObject().getVersions();
			}
        	@Override
        	public void setObject(String object) {
        		Set<String> set = Util.getSetFromString(object);
        		model.getObject().setVersions(Util.getStringFromSet(set));
        		holder.addVersions(set); // also add to the singleton that holds 'the' collection of versions
        	}
        });
        
        versionsTextField.add(new AutocompleterBehavior(VERSIONS));
        form.add(versionsTextField);

        // FIXME using a model, workaround - can't seem to use compoundpropertymodel with nested forms when creating (adding) a resource
        TextField<String> author = new TextField<String>("author", new Model<String>() {
        	@Override
        	public String getObject() {
        		return model.getObject().getAuthor();
        	}
        	
        	@Override
        	public void setObject(String object) {
        		model.getObject().setAuthor(object);
        	}
        	
        });
        author.add(StringValidator.maximumLength(45));
        form.add(author);
        
		form.add(new Image("captcha", captchaImageResource));
		form.add(new RequiredTextField<String>("captchaVerification", new PropertyModel<String>(this, "captchaVerification")));
		
		AjaxFallbackButton button = new AjaxFallbackButton("submit", form) {
        	@Override
	       	protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
        		target.addComponent(feedback);
	       	}       
        	@Override
			protected void onError(AjaxRequestTarget target, Form<?> form) {
				target.addComponent(feedback);
			}
        	
		};
		form.add(button);
		add(form);
		
		form.add(new Link<Void>("cancel") {
			@Override
			public void onClick() {
				setResponsePage(ListResourcePage.class);
			}
		});		
		
		form.add(new Link<Void>("delete") {
			@Override
			public void onClick() {
				Resource resource = model.getObject();
				log.info("[DELETE] Someone suggested deletion for module " + resource.getId());
				getSession().info(getString("message.delete"));
				setResponsePage(ListResourcePage.class);
			}
			
			@Override
			public boolean isVisible() {
				return !newResource;
			}
			
		});
		
	}
	
	private String randomString() {
    	Random r = new Random();
    	Integer i = r.nextInt(9);
    	return i.toString();
    }
	
	@SuppressWarnings("unchecked")
	private Panel getResourcePanel(String panelId, Class<? extends Resource> clazz) {
		
		Panel panel;
			
		if (Snippet.class.isAssignableFrom(clazz)) {
			panel = new EditSnippetPanel(panelId, (IModel) model);
		} else if (BlogPost.class.isAssignableFrom(clazz)) {
			panel = new EditBlogPostPanel(panelId, (IModel) model);
		} else { // for other resource, default Component
			panel = new EditComponentPanel(panelId, (IModel) model);
		}
		
		model = (IModel<Resource>) panel.getDefaultModel();
		return panel;
	}
	
}